package indi.mozping.atmoandcas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author by mozping
 * @Classname ABA
 * @Description TODO
 * @Date 2019/4/28 15:23
 */
public class ABA {

    private static AtomicInteger atomicInteger = new AtomicInteger(100);
    private static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100, 1);

    public static void main(String[] args) throws InterruptedException {

        // 1.线程at1悄悄的将atomicInteger改变了，然后又改了回来，
        Thread at1 = new Thread(new Runnable() {
            @Override
            public void run() {
                atomicInteger.compareAndSet(100, 110);
                atomicInteger.compareAndSet(110, 100);
            }
        });

        // 2.线程at1悄悄的将atomicInteger改变了，然后又改了回来，at2是感觉不到的
        Thread at2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(2);      // at1,执行完
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("使用AtomicInteger无法避免AB问题，我们修改成功了，说明我们没有意识到AtomicInteger已经被修改了: " + atomicInteger.compareAndSet(100, 120));
            }
        });

        at1.start();
        at2.start();

        at1.join();
        at2.join();

        // AtomicStampedReference
        // 3.线程tsf1悄悄的将atomicStampedReference改变了，然后又改了回来，但是版本号递增了
        Thread tsf1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //让 tsf2先获取stamp，导致预期时间戳不一致
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 预期引用：100，更新后的引用：110，预期标识getStamp() 更新后的标识getStamp() + 1
                atomicStampedReference.compareAndSet(100, 110, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
                atomicStampedReference.compareAndSet(110, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            }
        });

        // 4.线程tsf2悄悄的将atomicStampedReference改变了，然后又改了回来，但是版本号递增了,因此tsf2能够感知到，
        //因为他在sleep期间发现版本号被修改了
        Thread tsf2 = new Thread(new Runnable() {
            @Override
            public void run() {
                int stamp = atomicStampedReference.getStamp();

                try {
                    TimeUnit.SECONDS.sleep(2);      //线程tsf1执行完
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("使用AtomicStampedReference可以避免AB问题，我们修改失败了，因为我们发现版本戳被修改了: " + atomicStampedReference.compareAndSet(100, 120, stamp, stamp + 1));
            }
        });

        tsf1.start();
        tsf2.start();
    }
}
